/*
 * $Id: commands-tedit.cpp 412 2010-06-16 21:13:28Z cmtonkinson@gmail.com $
 *
 * This file is part of the Symphony project <http://code.google.com/p/symphonymud/>
 * Copyright 2005-2010 Chris Tonkinson <cmtonkinson@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "area.h"
#include "avatar.h"
#include "color-string.h"
#include "commands-tedit.h"
#include "commandTable.h"
#include "commandTable-default.h"
#include "display.h"
#include "handler.h"
#include "room.h"

#include "world.h"

/*
TCmd::TCmd( void ) {
  name( "" );
  level(  );
  addSyntax( 0, "" );
  brief( "" );
  return;
}

bool TCmd::execute( Creature* creature, const std::vector<std::string>& args ) {

  return true;
}
*/

TCmdAddline::TCmdAddline( void ) {
  name( "addline" );
  shortcut( "+" );
  addSyntax( -1, "<string>" );
  brief( "Adds the string on a new line of text." );
  return;
}

bool TCmdAddline::execute( Creature* creature, const std::vector<std::string>& args ) {
  std::vector<ColorString>* v = (std::vector<ColorString>*)avatar()->handler()->getState()["vector"];
  std::string newtext = ColorString( args[0] ).effectiveSubstr( 0, 79 );

  if ( v->size() == 1 && v->front().empty() ) {
    v->front() = newtext;
  } else {
    v->push_back( newtext );
  }

  avatar()->send( "Added \"{x%s\"{x\n", newtext.c_str() );

  return true;
}

TCmdAppend::TCmdAppend( void ) {
  name( "append" );
  addSyntax( -1, "<string>" );
  brief( "Appends the string to the last line of text, word-wrapping if needed." );
  return;
}

bool TCmdAppend::execute( Creature* creature, const std::vector<std::string>& args ) {
  std::string src = Regex::trim( args[0] );
  std::string* current_line = NULL;
  std::vector<ColorString>* v = (std::vector<ColorString>*)avatar()->handler()->getState()["vector"];
  size_t space_left = 0;
  size_t last_word = 0;

  // make sure we've got at least one line to start with (even if it's empty)
  if ( v->empty() ) {
    v->push_back( std::string() );
  }

  // ensure that the last line of text isn't too long
  if ( v->back().effectiveLength() >= 79 ) {
    v->push_back( std::string() );
  }

  while ( !src.empty() ) {
    src = Regex::trim( src );
    current_line = &(v->back());
    space_left = 79 - current_line->length();

    // is this line already full?
    if ( space_left < 2 ) {
      v->push_back( std::string() );
      continue;
    }

    // can we fit the whole text on the current line?
    if ( src.length() < space_left ) {
      if ( !current_line->empty() ) {
        current_line->append( 1, ' ' );
      }
      current_line->append( src );
      src.clear();
      continue;
    }

    // can any of the new text fit onto this line?
    if ( ( last_word = src.rfind( ' ', space_left ) ) == std::string::npos ) {
      v->push_back( std::string() );
      continue;
    }

    // move whatever we can from src into the current line
    if ( !current_line->empty() ) {
      current_line->append( 1, ' ' );
    }
    current_line->append( src, 0, last_word );
    src.erase( 0, last_word );
  }

  avatar()->send( "Text appended successfully.\n" );

  return true;
}

TCmdCancel::TCmdCancel( void ) {
  name( "cancel" );
  addSyntax( 0, "" );
  brief( "Discards the current editor session." );
  return;
}

bool TCmdCancel::execute( Creature* creature, const std::vector<std::string>& args ) {
  switch ( avatar()->composing().number() ) {
    case COMP_NOTE:
      avatar()->replaceHandler( new NoteCancelHandler( creature ) );
      break;
    default:
      avatar()->send( "You're not doing anything that can be 'cancelled.'" );
      return false;
      break;
  }
  return true;
}

TCmdDelete::TCmdDelete( void ) {
  name( "delete" );
  shortcut( "-" );
  addSyntax( 1, "<number>" );
  brief( "Erases a line of text." );
  return;
}

bool TCmdDelete::execute( Creature* creature, const std::vector<std::string>& args ) {
  std::vector<ColorString>* l = (std::vector<ColorString>*)avatar()->handler()->getState()["vector"];
  std::vector<ColorString> foo = *l;
  unsigned int line = estring(args[0]);
  unsigned int i = 0;

  if ( line < 1 || line > 99 ) {
    avatar()->send( "Invalid line number." );
    return false;
  }

  l->clear();

  for ( std::vector<ColorString>::iterator it = foo.begin(); it != foo.end(); ++it, ++i ) {
    if ( i != line-1 ) {
      l->push_back( *it );
    }
  }

  avatar()->send( "Deleted line %u\n", line );

  return true;
}

TCmdClear::TCmdClear( void ) {
  name( "clear" );
  addSyntax( 0, "" );
  brief( "Erases the entire text." );
  return;
}

bool TCmdClear::execute( Creature* creature, const std::vector<std::string>& args ) {
  ((std::vector<ColorString>*)avatar()->handler()->getState()["vector"])->clear();
  avatar()->send( "Text cleared." );
  return true;
}

TCmdInsert::TCmdInsert( void ) {
  name( "insert" );
  addSyntax( -2, "<number> <string>" );
  brief( "Inserts the given string in a new line of text, just before line <number>." );
  return;
}

bool TCmdInsert::execute( Creature* creature, const std::vector<std::string>& args ) {
  std::vector<ColorString>* l = (std::vector<ColorString>*)avatar()->handler()->getState()["vector"];
  unsigned int line = 0;
  unsigned int i = 0;

  line = estring( args[0] );

  if ( line < 1 || line > 99 ) {
    avatar()->send( "Invalid line number." );
    return false;
  }

  for ( std::vector<ColorString>::iterator it = l->begin(); it != l->end(); ++it, ++i ) {
    if ( i == line-1 ) {
      l->insert( it, ColorString(args[1]).effectiveSubstr( 0, 79 ) );
      avatar()->send( "Inserted text at line %u\n", line );
      return true;
    }
  }

  l->push_back( args[1] );
  avatar()->send( "Inserted text at line %u\n", line );

  return true;
}

TCmdReplace::TCmdReplace( void ) {
  name( "replace" );
  addSyntax( -2, "<number> <string>" );
  brief( "Replaces the line of text given by <number> with <string>." );
  return;
}

bool TCmdReplace::execute( Creature* creature, const std::vector<std::string>& args ) {
  TCmdDelete del;
  std::vector<std::string> del_args;
  TCmdInsert ins;
  std::vector<std::string> ins_args;

  del_args.push_back( args[0] );
  del.avatar( avatar() );
  del.execute( creature, del_args );
  ins_args.push_back( args[1] );
  ins.avatar( avatar() );
  ins.execute( creature, ins_args );
  return true;
}

TCmdSend::TCmdSend( void ) {
  name( "send" );
  addSyntax( 0, "" );
  brief( "Finalizes a note and posts it." );
  return;
}

bool TCmdSend::execute( Creature* creature, const std::vector<std::string>& args ) {
  switch ( avatar()->composing().number() ) {
    case COMP_NOTE:
      avatar()->replaceHandler( new NoteSendHandler( creature ) );
      break;
    default:
      avatar()->send( "You're not doing anything that can be 'sent.'" );
      return false;
      break;
  }
  return true;
}

TCmdShow::TCmdShow( void ) {
  name( "show" );
  addSyntax( 0, "" );
  brief( "Displays the entire text at once." );
  return;
}

bool TCmdShow::execute( Creature* creature, const std::vector<std::string>& args ) {
  std::vector<ColorString>* l = (std::vector<ColorString>*)avatar()->handler()->getState()["vector"];
  char buffer[4096];
  std::string output;
  unsigned int u = 0;
  unsigned int len = 0;

  if ( l->empty() ) {
    avatar()->send( "There is no text in the buffer at the moment." );
    return true;
  }

  output.append( "+--+" ).append( 82, '-' ).append( "+\n" );
  for ( std::vector<ColorString>::iterator it = l->begin(); it != l->end(); ++it ) {
    try {
      len = it->effectiveLength();
      sprintf( buffer, "|{y%02u{x| ", ++u );
      output.append( buffer ).append( (*it).c_str() ).append( 80-len, ' ' ).append( " |\n" );
    } catch ( std::exception e ) {
      fprintf( stderr, "TCmdShow::execute(): \"%s\" buffer was \"%s\" len was %u", e.what(), buffer, len );
    }
  }
  output.append( "+--+" ).append( 82, '-' ).append( "+\n" );

  avatar()->send( output );
  return true;
}
